﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Chunk : MonoBehaviour
{
    public Index index;
    public ushort[,,] VoxelTypeList;
    private ChunkRender chunkRender;
    private bool initialized = false;

    void Awake()
    {
        VoxelTypeList = new ushort[Const.ChunkSideLength, Const.ChunkSideLength, Const.ChunkSideLength];
        index = new Index(transform.position);
        transform.position = transform.position * Const.ChunkSideLength;
        // GenerateVoxelData();
    }
    void Start()
    {
        GenerateVoxelData();
        chunkRender = new ChunkRender(this);
        // chunkRender.Render();
    }

    // Update is called once per frame
    void Update()
    {
    }
    void LateUpdate()
    {
        if (!initialized)
        {
            chunkRender.Render();
            initialized = true;
        }
    }
    public void ReDraw()
    {
        chunkRender.Render();
    }
    public void SetVoxelSimple(int x, int y, int z, ushort voxelTypeID)
    {
        try
        {
            VoxelTypeList[x, y, z] = voxelTypeID;
        }
        catch (System.Exception)
        {
            Debug.LogError("Chunk SetVoxelSimple");
        }
    }
    public void SetVoxelSimple(Index index, ushort voxelTypeID)
    {
        SetVoxelSimple(index.x, index.y, index.z, voxelTypeID);
    }
    public ushort GetVoxelSimple(int x, int y, int z)
    {
        try
        {
            ushort? voxelTypeID = VoxelTypeList[x, y, z];
            return voxelTypeID ?? 0;
        }
        catch (System.Exception)
        {
            Debug.LogError("Chunk GetVoxelSimple not Found Voxel " + x + " " + y + " " + z);
            return 0;
        }
    }
    public ushort GetVoxelSimple(Index index)
    {
        return GetVoxelSimple(index.x, index.y, index.z);
    }
    public void SetVoxelTypeID(int x, int y, int z, ushort voxelTypeID, bool reDraw = false)
    {
        Index rightIndex = new Index(index.x, index.y, index.z);
        int rightX = x, rightY = y, rightZ = z;
        ushort sideLength = Const.ChunkSideLength;
        if (x < 0)
        {
            rightIndex.x -= 1; rightX += sideLength;
        }
        else if (x >= sideLength)
        {
            rightIndex.x += 1; rightX -= sideLength;
        }
        if (y < 0)
        {
            rightIndex.y -= 1; rightY += sideLength;
        }
        else if (y >= sideLength)
        {
            rightIndex.y += 1; rightY -= sideLength;
        }
        if (z < 0)
        {
            rightIndex.z -= 1; rightZ += sideLength;
        }
        else if (z >= sideLength)
        {
            rightIndex.z += 1; rightZ -= sideLength;
        }
        if (((rightX - x) | (rightY - y) | (rightZ - z)) != 0)
        {
            Chunk chunk = BlocksTerrain.Instance.ChunkManager.GetChunk(rightIndex);
            if (chunk == null)
            {
                Debug.LogWarning("Chunk SetVoxelTypeID not Found Chunk " + x + " " + y + " " + z);
            }
            else
            {
                chunk.SetVoxelTypeID(rightX, rightY, rightZ, voxelTypeID);
                if (reDraw) { chunk.ReDraw(); }
            }
            return;
        }
        SetVoxelSimple(x, y, z, voxelTypeID);
        if (reDraw) { ReDraw(); }
    }
    public void SetVoxelTypeID(Index index, ushort voxelTypeID, bool reDraw = false)
    {
        SetVoxelTypeID(index.x, index.y, index.z, voxelTypeID, reDraw);
    }
    public ushort GetVoxelTypeID(int x, int y, int z)
    {
        Index rightIndex = new Index(index.x, index.y, index.z);
        int rightX = x, rightY = y, rightZ = z;
        ushort sideLength = Const.ChunkSideLength;
        if (x < 0)
        {
            rightIndex.x -= 1; rightX += sideLength;
        }
        else if (x >= sideLength)
        {
            rightIndex.x += 1; rightX -= sideLength;
        }
        if (y < 0)
        {
            rightIndex.y -= 1; rightY += sideLength;
        }
        else if (y >= sideLength)
        {
            rightIndex.y += 1; rightY -= sideLength;
        }
        if (z < 0)
        {
            rightIndex.z -= 1; rightZ += sideLength;
        }
        else if (z >= sideLength)
        {
            rightIndex.z += 1; rightZ -= sideLength;
        }
        if (((rightX - x) | (rightY - y) | (rightZ - z)) != 0)
        {
            Chunk chunk = BlocksTerrain.Instance.ChunkManager.GetChunk(rightIndex);
            if (chunk == null)
            {
                // Debug.LogWarning("Chunk GetVoxelTypeID not Found Chunk " + x + " " + y + " " + z);
                return 0;
            }
            else
            {
                return chunk.GetVoxelTypeID(rightX, rightY, rightZ);
            }
        }
        return GetVoxelSimple(x, y, z);
    }
    public ushort GetVoxelTypeID(Index index)
    {
        return GetVoxelTypeID(index.x, index.y, index.z);
    }
    public Vector3 VoxelIndexToPosition(int x, int y, int z)
    { // Voxel 单位是1
        return transform.TransformPoint(new Vector3(x, y, z));
    }
    public Vector3 VoxelIndexToPosition(Index index)
    {
        return VoxelIndexToPosition(index.x, index.y, index.z);
    }

    public Index PositionToVoxelIndex(Vector3 position, Vector3 normal, bool needAdjacent)
    {
        position += normal * (needAdjacent ? 0.25f : -0.25f);
        position = transform.InverseTransformPoint(position);
        return new Index(Mathf.RoundToInt(position.x)
            , Mathf.RoundToInt(position.y)
            , Mathf.RoundToInt(position.z)
        );
    }

    private void GenerateVoxelData()
    {
        int seed = BlocksTerrain.Instance.Seed;
        ushort sideLength = Const.ChunkSideLength;
        int currentBaseHeight = index.y * sideLength;
        for (int x = 0; x < sideLength; ++x)
        {
            for (int z = 0; z < sideLength; ++z)
            {
                Vector3 position = VoxelIndexToPosition(x, 0, z);
                position.x += seed; position.z += seed;
                float perlin1 = Mathf.PerlinNoise(position.x * 0.010f, position.z * 0.010f) * 70.1f;
                float perlin2 = Mathf.PerlinNoise(position.x * 0.085f, position.z * 0.085f) * 9.1f;
                for (int y = 0; y < sideLength; ++y)
                {
                    if ((currentBaseHeight + y + perlin2 + 1) < perlin1)
                    {
                        SetVoxelSimple(x, y, z, Const.VoxelType_Clod);
                    }
                    else if ((currentBaseHeight + y + perlin2) < perlin1)
                    {
                        if (CheckCanAddTree(x, y, z) && (Random.Range(0.0f, 1.0f) < Const.TreeRate))
                        {
                            AddTree(x, y, z);
                        }
                        else
                        {
                            SetVoxelSimple(x, y, z, Const.VoxelType_Grass);
                        }
                    }
                }
            }
        }
    }

    private bool CheckCanAddTree(int x, int y, int z)
    {
        int maxPos = Const.ChunkSideLength - 2;
        return (((x - 1) | (maxPos - x) | (y - 1) | (Const.ChunkSideLength - 6 - y) | (z - 1) | (maxPos - z)) >= 0);
    }

    private void AddTree(int x, int y, int z)
    {
        for (int i = 0; i < 4; ++i)
        {
            SetVoxelSimple(x, y + i, z, Const.VoxelType_Wood);
        }
        for (int offsetY = 2; offsetY < 4; ++offsetY)
        {
            for (int offsetX = -1; offsetX <= 1; ++offsetX)
            {
                for (int offsetZ = -1; offsetZ <= 1; ++offsetZ)
                {
                    if ((offsetX | offsetZ) != 0)
                    {
                        // if (!((offsetX == 0) && (offsetZ == 0)))
                        SetVoxelSimple(x + offsetX, y + offsetY, z + offsetZ, Const.VoxelType_Leaves);
                    }
                }
            }
            SetVoxelSimple(x, y + 4, z, Const.VoxelType_Leaves);
        }
    }
}
